<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/ice.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/ice.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"yoursite.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":3,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="参考文档：https:&#x2F;&#x2F;cyc2018.github.io&#x2F;CS-Notes&#x2F;#&#x2F;notes&#x2F;数据库系统原理  事务概念事务指的是满足 ACID 特性的一组操作，可以通过 Commit 提交一个事务，也可以使用 Rollback 进行回滚。">
<meta property="og:type" content="article">
<meta property="og:title" content="数据库系统原理">
<meta property="og:url" content="http://yoursite.com/2020/05/30/%E6%95%B0%E6%8D%AE%E5%BA%93/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86/index.html">
<meta property="og:site_name" content="严冰的博客">
<meta property="og:description" content="参考文档：https:&#x2F;&#x2F;cyc2018.github.io&#x2F;CS-Notes&#x2F;#&#x2F;notes&#x2F;数据库系统原理  事务概念事务指的是满足 ACID 特性的一组操作，可以通过 Commit 提交一个事务，也可以使用 Rollback 进行回滚。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pnlokjqj30us0jbq3x.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgh34gyj30uy0fndgv.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgi2qgmj30tu0hiaaq.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgii73mj30r10mygmh.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgjghfkj30ra0l1mxw.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgkbmzwj30sb0kwt9o.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgkr46bj30tw0d2mxl.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pglpg3tj30tz0g474z.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgmltgij30vd0q0wfs.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgnk9nqj30u70qiabe.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgp1gpij30t50sawft.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgpwa4dj30v10gb0u1.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgqfcvhj30ty0aoq3m.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgraso5j30tf0dhgm3.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgrwfhuj30f703y74a.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgsso8aj309j079wel.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgt8i9mj30fd06omx9.jpg">
<meta property="og:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgu7d52j30l8090t92.jpg">
<meta property="article:published_time" content="2020-05-30T12:50:49.000Z">
<meta property="article:modified_time" content="2020-06-08T00:54:53.391Z">
<meta property="article:author" content="yanbing">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pnlokjqj30us0jbq3x.jpg">

<link rel="canonical" href="http://yoursite.com/2020/05/30/%E6%95%B0%E6%8D%AE%E5%BA%93/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>数据库系统原理 | 严冰的博客</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/atom.xml" title="严冰的博客" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">严冰的博客</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2020/05/30/%E6%95%B0%E6%8D%AE%E5%BA%93/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/dog.jpg">
      <meta itemprop="name" content="yanbing">
      <meta itemprop="description" content="闲看庭前花开落，漫随天外云卷舒。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="严冰的博客">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          数据库系统原理
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2020-05-30 20:50:49" itemprop="dateCreated datePublished" datetime="2020-05-30T20:50:49+08:00">2020-05-30</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-06-08 08:54:53" itemprop="dateModified" datetime="2020-06-08T08:54:53+08:00">2020-06-08</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/" itemprop="url" rel="index"><span itemprop="name">数据库</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <blockquote>
<p>  参考文档：<a href="https://cyc2018.github.io/CS-Notes/#/notes/数据库系统原理" target="_blank" rel="noopener">https://cyc2018.github.io/CS-Notes/#/notes/数据库系统原理</a></p>
</blockquote>
<h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><p>事务指的是满足 ACID 特性的一组操作，可以通过 Commit 提交一个事务，也可以使用 Rollback 进行回滚。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pnlokjqj30us0jbq3x.jpg" alt="img" style="zoom:50%;" />

<a id="more"></a>

<h3 id="ACID特性"><a href="#ACID特性" class="headerlink" title="ACID特性"></a>ACID特性</h3><h4 id="1-原子性（Atomicity）"><a href="#1-原子性（Atomicity）" class="headerlink" title="1. 原子性（Atomicity）"></a>1. 原子性（Atomicity）</h4><p>事务被视为<strong>不可分割的最小单元</strong>，事务的所有操作要么全部提交成功，要么全部失败回滚。</p>
<p>回滚可以用回滚日志（Undo Log）来实现，回滚日志记录着事务所执行的修改操作，在回滚时反向执行这些修改操作即可。</p>
<h4 id="2-一致性（Consistency）"><a href="#2-一致性（Consistency）" class="headerlink" title="2. 一致性（Consistency）"></a>2. 一致性（Consistency）</h4><p>数据库在事务执行前后都保持一致性状态。在一致性状态下，所有事务对同一个数据的读取结果都是相同的。</p>
<h4 id="3-隔离性（Isolation）"><a href="#3-隔离性（Isolation）" class="headerlink" title="3. 隔离性（Isolation）"></a>3. 隔离性（Isolation）</h4><p>一个事务所做的修改在最终提交以前，对其它事务是不可见的。</p>
<h4 id="4-持久性（Durability）"><a href="#4-持久性（Durability）" class="headerlink" title="4. 持久性（Durability）"></a>4. 持久性（Durability）</h4><p>一旦事务提交，则其所做的修改将会永远保存到数据库中。即使系统发生崩溃，事务执行的结果也不能丢失。</p>
<p>系统发生奔溃可以用重做日志（Redo Log）进行恢复，从而实现持久性。与回滚日志记录数据的逻辑修改不同，重做日志记录的是数据页的物理修改。</p>
<hr>
<p>事务的 ACID 特性概念简单，但不是很好理解，主要是因为这几个特性不是一种平级关系：</p>
<ul>
<li>只有满足一致性，事务的执行结果才是正确的。</li>
<li>在无并发的情况下，事务串行执行，隔离性一定能够满足。此时只要能满足原子性，就一定能满足一致性。</li>
<li>在并发的情况下，多个事务并行执行，事务不仅要满足原子性，还需要满足隔离性，才能满足一致性。</li>
<li>事务满足持久化是为了能应对系统崩溃的情况。</li>
</ul>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgh34gyj30uy0fndgv.jpg" alt="img" style="zoom:50%;" />



<h3 id="AUTOCOMMIT"><a href="#AUTOCOMMIT" class="headerlink" title="AUTOCOMMIT"></a>AUTOCOMMIT</h3><p>MySQL 默认采用自动提交模式。也就是说，如果不显式使用<code>START TRANSACTION</code>语句来开始一个事务，那么每个查询操作都会被当做一个事务并自动提交。</p>
<h2 id="并发一致性问题"><a href="#并发一致性问题" class="headerlink" title="并发一致性问题"></a>并发一致性问题</h2><p>在并发环境下，事务的隔离性很难保证，因此会出现很多并发一致性问题。</p>
<h3 id="丢失修改"><a href="#丢失修改" class="headerlink" title="丢失修改"></a>丢失修改</h3><p>T1 和 T2 两个事务都对一个数据进行修改，T1 先修改，T2 随后修改，T2 的修改覆盖了 T1 的修改。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgi2qgmj30tu0hiaaq.jpg" alt="img" style="zoom:50%;" />

<h3 id="读脏数据"><a href="#读脏数据" class="headerlink" title="读脏数据"></a>读脏数据</h3><p>T1 修改一个数据，T2 随后读取这个数据。如果 T1 撤销了这次修改，那么 T2 读取的数据是脏数据。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgii73mj30r10mygmh.jpg" alt="img" style="zoom:50%;" />

<h3 id="不可重复读"><a href="#不可重复读" class="headerlink" title="不可重复读"></a>不可重复读</h3><p>T2 读取一个数据，T1 对该数据做了修改。如果 T2 再次读取这个数据，此时读取的结果和第一次读取的结果不同。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgjghfkj30ra0l1mxw.jpg" alt="img" style="zoom:50%;" />

<h3 id="幻影读"><a href="#幻影读" class="headerlink" title="幻影读"></a>幻影读</h3><p>T1 读取某个<strong>范围</strong>的数据，T2 在这个<strong>范围内</strong>插入新的数据，T1 再次读取这个范围的数据，此时读取的结果和和第一次读取的结果不同。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgkbmzwj30sb0kwt9o.jpg" alt="img" style="zoom:50%;" />

<hr>
<p>产生并发不一致性问题的主要原因是破坏了事务的隔离性，解决方法是通过并发控制来保证隔离性。并发控制可以通过封锁来实现，但是封锁操作需要用户自己控制，相当复杂。数据库管理系统提供了事务的隔离级别，让用户以一种更轻松的方式处理并发一致性问题。</p>
<h2 id="封锁"><a href="#封锁" class="headerlink" title="封锁"></a>封锁</h2><h3 id="封锁粒度"><a href="#封锁粒度" class="headerlink" title="封锁粒度"></a>封锁粒度</h3><p>MySQL 中提供了两种封锁粒度：<strong>行级锁</strong>以及<strong>表级锁</strong>。</p>
<p>应该尽量只锁定需要修改的那部分数据，而不是所有的资源。锁定的数据量越少，发生锁争用的可能就越小，系统的并发程度就越高。</p>
<p>但是加锁需要消耗资源，锁的各种操作（包括获取锁、释放锁、以及检查锁状态）都会增加系统开销。因此封锁粒度越小，系统开销就越大。</p>
<p>在选择封锁粒度时，需要在锁开销和并发程度之间做一个权衡。</p>
<h3 id="封锁类型"><a href="#封锁类型" class="headerlink" title="封锁类型"></a>封锁类型</h3><h4 id="1-读写锁"><a href="#1-读写锁" class="headerlink" title="1. 读写锁"></a>1. 读写锁</h4><ul>
<li>互斥锁（Exclusive），简写为 X 锁，又称写锁。</li>
<li>共享锁（Shared），简写为 S 锁，又称读锁。</li>
</ul>
<p>有以下两个规定：</p>
<ul>
<li>一个事务对数据对象 A 加了 <strong>X 锁</strong>，就可以对 A 进行<strong>读取</strong>和<strong>更新</strong>。<strong>加锁期间其它事务不能对 A 加任何锁</strong>。</li>
<li>一个事务对数据对象 A 加了 <strong>S 锁</strong>，可以对 A 进行<strong>读取</strong>操作，但是不能进行更新操作。<strong>加锁期间其它事务能对 A 加 S 锁，但是不能加 X 锁</strong>。</li>
</ul>
<p>锁的兼容关系如下：</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgkr46bj30tw0d2mxl.jpg" alt="img" style="zoom:50%;" />

<h4 id="2-意向锁"><a href="#2-意向锁" class="headerlink" title="2. 意向锁"></a>2. 意向锁</h4><p>使用意向锁（Intention Locks）可以更容易地支持多粒度封锁。</p>
<p>在存在行级锁和表级锁的情况下，事务 T 想要对表 A 加 X 锁，就需要先检测是否有其它事务对表 A 或者表 A 中的任意一行加了锁，那么就需要对表 A 的每一行都检测一次，这是非常耗时的。</p>
<p>意向锁在原来的 X/S 锁之上引入了 IX/IS，<strong>IX/IS 都是表锁</strong>，用来表示一个事务想要在表中的某个数据行上加 X 锁或 S 锁。有以下两个规定：</p>
<ul>
<li>一个事务在获得某个数据行对象的 S 锁之前，必须先获得表的 IS 锁或者更强的锁；</li>
<li>一个事务在获得某个数据行对象的 X 锁之前，必须先获得表的 IX 锁。</li>
</ul>
<p>通过引入意向锁，事务 T 想要对表 A 加 X 锁，只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁，如果加了就表示有其它事务正在使用这个表或者表中某一行的锁，因此事务 T 加 X 锁失败。</p>
<p>各种锁的兼容关系如下：</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pglpg3tj30tz0g474z.jpg" alt="img" style="zoom:50%;" />



<p>解释如下：</p>
<ul>
<li>任意 IS/IX 锁之间都是兼容的，因为它们<strong>只表示想要对表加锁，而不是真正加锁</strong>；</li>
<li>这里兼容关系针对的是表级锁，而表级的 IX 锁和行级的 X 锁兼容，两个事务可以对两个数据行加 X 锁。（事务 T1 想要对数据行 R1 加 X 锁，事务 T2 想要对同一个表的数据行 R2 加 X 锁，两个事务都需要对该表加 IX 锁，但是 IX 锁是兼容的，并且 IX 锁与行级的 X 锁也是兼容的，因此两个事务都能加锁成功，对同一个表中的两个数据行做修改。）</li>
</ul>
<h3 id="封锁协议"><a href="#封锁协议" class="headerlink" title="封锁协议"></a>封锁协议</h3><h4 id="三级封锁协议"><a href="#三级封锁协议" class="headerlink" title="三级封锁协议"></a>三级封锁协议</h4><p><strong>一级封锁协议</strong></p>
<p>事务 T 要修改数据 A 时必须加 X 锁，直到 T 结束才释放锁。</p>
<p>可以<strong>解决丢失修改问题</strong>，因为不能同时有两个事务对同一个数据进行修改，那么事务的修改就不会被覆盖。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgmltgij30vd0q0wfs.jpg" alt="img" style="zoom:50%;" />

<p><strong>二级封锁协议</strong></p>
<p>在一级的基础上，要求读取数据 A 时必须加 S 锁，读取完马上释放 S 锁。</p>
<p>可以<strong>解决读脏数据问题</strong>，因为如果一个事务在对数据 A 进行修改，根据 1 级封锁协议，会加 X 锁，那么就不能再加 S 锁了，也就是不会读入数据。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgnk9nqj30u70qiabe.jpg" alt="img" style="zoom:50%;" />

<p><strong>三级封锁协议</strong></p>
<p>在二级的基础上，要求读取数据 A 时必须加 S 锁，<strong>直到事务结束</strong>了才能释放 S 锁。</p>
<p>可以<strong>解决不可重复读的问题</strong>，因为读 A 时，其它事务不能对 A 加 X 锁，从而避免了在读的期间数据发生改变。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgp1gpij30t50sawft.jpg" alt="img" style="zoom:50%;" />

<h4 id="两段封锁协议"><a href="#两段封锁协议" class="headerlink" title="两段封锁协议"></a>两段封锁协议</h4><p>加锁和解锁分为两个阶段进行。</p>
<p>可串行化调度是指，通过并发控制，使得并发执行的事务结果与某个串行执行的事务结果相同。串行执行的事务互不干扰，不会出现并发一致性问题。</p>
<p>事务遵循两段锁协议是保证可串行化调度的充分条件。例如以下操作满足两段锁协议，它是可串行化调度。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B)</span><br></pre></td></tr></table></figure>

<p>但不是必要条件，例如以下操作不满足两段锁协议，但它还是可串行化调度。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C)</span><br></pre></td></tr></table></figure>

<h3 id="MySQL-隐式与显示锁定"><a href="#MySQL-隐式与显示锁定" class="headerlink" title="MySQL 隐式与显示锁定"></a>MySQL 隐式与显示锁定</h3><p>MySQL 的 <strong>InnoDB 存储引擎采用两段锁协议</strong>，会根据隔离级别在需要的时候自动加锁，并且所有的锁都是在同一时刻被释放，这被称为隐式锁定。</p>
<p>InnoDB 也可以使用特定的语句进行显示锁定：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> ... <span class="keyword">LOCK</span> <span class="keyword">In</span> <span class="keyword">SHARE</span> <span class="keyword">MODE</span>;</span><br><span class="line"><span class="keyword">SELECT</span> ... <span class="keyword">FOR</span> <span class="keyword">UPDATE</span>;</span><br></pre></td></tr></table></figure>

<h2 id="隔离级别"><a href="#隔离级别" class="headerlink" title="隔离级别"></a>隔离级别</h2><h3 id="未提交读（READ-UNCOMMITTED）"><a href="#未提交读（READ-UNCOMMITTED）" class="headerlink" title="未提交读（READ UNCOMMITTED）"></a>未提交读（READ UNCOMMITTED）</h3><p>事务中的修改，即使没有提交，对其它事务也是可见的。</p>
<h3 id="提交读（READ-COMMITTED）"><a href="#提交读（READ-COMMITTED）" class="headerlink" title="提交读（READ COMMITTED）"></a>提交读（READ COMMITTED）</h3><p>一个事务只能读取已经提交的事务所做的修改。换句话说，一个事务所做的修改在提交之前对其它事务是不可见的。</p>
<h3 id="可重复读（REPEATABLE-READ）"><a href="#可重复读（REPEATABLE-READ）" class="headerlink" title="可重复读（REPEATABLE READ）"></a>可重复读（REPEATABLE READ）</h3><p>保证在同一个事务中多次读取同一数据的结果是一样的。</p>
<h3 id="可串行化（SERIALIZABLE）"><a href="#可串行化（SERIALIZABLE）" class="headerlink" title="可串行化（SERIALIZABLE）"></a>可串行化（SERIALIZABLE）</h3><p>强制事务串行执行，这样多个事务互不干扰，不会出现并发一致性问题。</p>
<p>该隔离级别需要加锁实现，因为要使用加锁机制保证同一时间只有一个事务执行，也就是保证事务串行执行。</p>
<hr>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgpwa4dj30v10gb0u1.jpg" alt="img" style="zoom:50%;" />

<h2 id="多版本并发控制"><a href="#多版本并发控制" class="headerlink" title="多版本并发控制"></a>多版本并发控制</h2><p>多版本并发控制（Multi-Version Concurrency Control, MVCC）是 MySQL 的 <strong>InnoDB 存储引擎实现隔离级别的一种具体方式</strong>，<strong>用于实现提交读和可重复读这两种隔离级别</strong>。而未提交读隔离级别总是读取最新的数据行，要求很低，无需使用 MVCC。可串行化隔离级别需要对所有读取的行都加锁，单纯使用 MVCC 无法实现。</p>
<h3 id="基本思想"><a href="#基本思想" class="headerlink" title="基本思想"></a>基本思想</h3><p>在封锁一节中提到，加锁能解决多个事务同时执行时出现的并发一致性问题。在实际场景中读操作往往多于写操作，因此又引入了读写锁来避免不必要的加锁操作，例如读和读没有互斥关系。读写锁中读和写操作仍然是互斥的，而 <strong>MVCC 利用了多版本的思想，写操作更新最新的版本快照，而读操作去读旧版本快照，没有互斥关系</strong>，这一点和 CopyOnWrite 类似。</p>
<p>在 MVCC 中事务的<strong>修改操作（DELETE、INSERT、UPDATE）会为数据行新增一个版本快照</strong>。</p>
<p>脏读和不可重复读最根本的原因是事务读取到其它事务未提交的修改。在事务进行读取操作时，为了解决脏读和不可重复读问题，<strong>MVCC 规定只能读取已经提交的快照</strong>。当然一个事务可以读取自身未提交的快照，这不算是脏读。</p>
<h3 id="版本号"><a href="#版本号" class="headerlink" title="版本号"></a>版本号</h3><ul>
<li>系统版本号 SYS_ID：是一个递增的数字，每开始一个新的事务，系统版本号就会自动递增。</li>
<li>事务版本号 TRX_ID ：事务开始时的系统版本号。</li>
</ul>
<h3 id="Undo-日志"><a href="#Undo-日志" class="headerlink" title="Undo 日志"></a>Undo 日志</h3><p>MVCC 的多版本指的是多个版本的快照，<strong>快照存储在 Undo 日志中</strong>，该日志通过回滚指针 <code>ROLL_PTR</code> 把一个数据行的所有快照连接起来。</p>
<p>例如在 MySQL 创建一个表 t，包含主键 id 和一个字段 x。我们先插入一个数据行，然后对该数据行执行两次更新操作。</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> t(<span class="keyword">id</span>, x) <span class="keyword">VALUES</span>(<span class="number">1</span>, <span class="string">"a"</span>);</span><br><span class="line"><span class="keyword">UPDATE</span> t <span class="keyword">SET</span> x=<span class="string">"b"</span> <span class="keyword">WHERE</span> <span class="keyword">id</span>=<span class="number">1</span>;</span><br><span class="line"><span class="keyword">UPDATE</span> t <span class="keyword">SET</span> x=<span class="string">"c"</span> <span class="keyword">WHERE</span> <span class="keyword">id</span>=<span class="number">1</span>;</span><br></pre></td></tr></table></figure>

<p>因为没有使用 <code>START TRANSACTION</code> 将上面的操作当成一个事务来执行，根据 MySQL 的 AUTOCOMMIT 机制，每个操作都会被当成一个事务来执行，所以上面的操作总共涉及到三个事务。快照中除了记录事务版本号 TRX_ID 和操作之外，还记录了一个 bit 的 DEL 字段，用于标记是否被删除。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgqfcvhj30ty0aoq3m.jpg" alt="img" style="zoom:50%;" />

<p>INSERT、UPDATE、DELETE 操作会创建一个日志，并将事务版本号 TRX_ID 写入。DELETE 可以看成是一个特殊的 UPDATE，还会额外将 DEL 字段设置为 1。</p>
<h3 id="ReadView"><a href="#ReadView" class="headerlink" title="ReadView"></a>ReadView</h3><p>MVCC 维护了一个 <code>ReadView</code> 结构，主要包含了当前系统未提交的事务列表 <code>TRX_IDs {TRX_ID_1, TRX_ID_2, ...}</code>，还有该列表的最小值 <code>TRX_ID_MIN</code> 和 <code>TRX_ID_MAX</code>。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgraso5j30tf0dhgm3.jpg" alt="img" style="zoom:50%;" />

<p>在进行 SELECT 操作时，根据数据行快照的 <code>TRX_ID</code> 与 <code>TRX_ID_MIN</code> 和 <code>TRX_ID_MAX</code> 之间的关系，从而判断数据行快照是否可以使用：</p>
<ul>
<li><code>TRX_ID &lt; TRX_ID_MIN</code>，表示该数据行快照时在当前所有未提交事务之前进行更改的，因此可以使用。</li>
<li><code>TRX_ID &gt; TRX_ID_MAX</code>，表示该数据行快照是在事务启动之后被更改的，因此不可使用。</li>
<li><code>TRX_ID_MIN</code> &lt;= <code>TRX_ID</code> &lt;= <code>TRX_ID_MAX</code>，需要根据隔离级别再进行判断：<ul>
<li>提交读：如果 <code>TRX_ID</code> 在 <code>TRX_IDs</code> 列表中，表示该数据行快照对应的事务还未提交，则该快照不可使用。否则表示已经提交，可以使用。</li>
<li>可重复读：都不可以使用。因为如果可以使用的话，那么其它事务也可以读到这个数据行快照并进行修改，那么当前事务再去读这个数据行得到的值就会发生改变，也就是出现了不可重复读问题。</li>
</ul>
</li>
</ul>
<p>在数据行快照不可使用的情况下，需要沿着 Undo Log 的回滚指针 ROLL_PTR 找到下一个快照，再进行上面的判断。</p>
<h3 id="快照读与当前读"><a href="#快照读与当前读" class="headerlink" title="快照读与当前读"></a>快照读与当前读</h3><h4 id="1-快照读"><a href="#1-快照读" class="headerlink" title="1. 快照读"></a>1. 快照读</h4><p>MVCC 的 SELECT 操作是快照中的数据，不需要进行加锁操作。</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">table</span> ...;</span><br></pre></td></tr></table></figure>

<h4 id="2-当前读"><a href="#2-当前读" class="headerlink" title="2. 当前读"></a>2. 当前读</h4><p>MVCC 其它会对数据库进行修改的操作（INSERT、UPDATE、DELETE）需要进行加锁操作，从而读取最新的数据。可以看到 MVCC 并不是完全不用加锁，而只是避免了 SELECT 的加锁操作。</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">INSERT</span>;</span><br><span class="line"><span class="keyword">UPDATE</span>;</span><br><span class="line"><span class="keyword">DELETE</span>;</span><br></pre></td></tr></table></figure>

<p>在进行 SELECT 操作时，可以强制指定进行加锁操作。以下第一个语句需要加 S 锁，第二个需要加 X 锁。</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">table</span> <span class="keyword">WHERE</span> ? <span class="keyword">lock</span> <span class="keyword">in</span> <span class="keyword">share</span> <span class="keyword">mode</span>;</span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">table</span> <span class="keyword">WHERE</span> ? <span class="keyword">for</span> <span class="keyword">update</span>;</span><br></pre></td></tr></table></figure>

<h2 id="Next-Key-Locks"><a href="#Next-Key-Locks" class="headerlink" title="Next-Key Locks"></a>Next-Key Locks</h2><p><strong>Next-Key Locks 是 MySQL 的 InnoDB 存储引擎的一种锁实现</strong>。</p>
<p>MVCC 不能解决幻影读问题，Next-Key Locks 就是为了解决这个问题而存在的。<strong>在可重复读（REPEATABLE READ）隔离级别下，使用 MVCC + Next-Key Locks 可以解决幻读问题。</strong></p>
<h3 id="Record-Locks"><a href="#Record-Locks" class="headerlink" title="Record Locks"></a>Record Locks</h3><p><strong>锁定一个记录上的索引，而不是记录本身。</strong></p>
<p>如果表没有设置索引，InnoDB 会自动在主键上创建隐藏的聚簇索引，因此 Record Locks 依然可以使用。</p>
<h3 id="Gap-Locks"><a href="#Gap-Locks" class="headerlink" title="Gap Locks"></a>Gap Locks</h3><p><strong>锁定索引之间的间隙，但是不包含索引本身</strong>。例如当一个事务执行以下语句，其它事务就不能在 t.c 中插入 15。</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> c <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> c <span class="keyword">BETWEEN</span> <span class="number">10</span> <span class="keyword">and</span> <span class="number">20</span> <span class="keyword">FOR</span> <span class="keyword">UPDATE</span>;</span><br></pre></td></tr></table></figure>

<h3 id="Next-Key-Locks-1"><a href="#Next-Key-Locks-1" class="headerlink" title="Next-Key Locks"></a>Next-Key Locks</h3><p>它是 Record Locks 和 Gap Locks 的结合，不仅锁定一个记录上的索引，也锁定索引之间的间隙。它锁定一个前开后闭区间，例如一个索引包含以下值：10, 11, 13, and 20，那么就需要锁定以下区间：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">(-∞, 10]</span><br><span class="line">(10, 11]</span><br><span class="line">(11, 13]</span><br><span class="line">(13, 20]</span><br><span class="line">(20, +∞)</span><br></pre></td></tr></table></figure>

<h2 id="关系数据库设计理论"><a href="#关系数据库设计理论" class="headerlink" title="关系数据库设计理论"></a>关系数据库设计理论</h2><h3 id="函数依赖"><a href="#函数依赖" class="headerlink" title="函数依赖"></a>函数依赖</h3><p>记 A-&gt;B 表示 A 函数决定 B，也可以说 B 函数依赖于 A。</p>
<p>如果 {A1，A2，… ，An} 是关系的一个或多个属性的集合，该集合函数决定了关系的其它所有属性并且是最小的，那么该集合就称为键码。</p>
<p>对于 A-&gt;B，如果能找到 A 的真子集 A’，使得 A’-&gt; B，那么 A-&gt;B 就是部分函数依赖，否则就是完全函数依赖。</p>
<p>对于 A-&gt;B，B-&gt;C，则 A-&gt;C 是一个传递函数依赖。</p>
<h3 id="异常"><a href="#异常" class="headerlink" title="异常"></a>异常</h3><p>以下的学生课程关系的函数依赖为 {Sno, Cname} -&gt; {Sname, Sdept, Mname, Grade}，键码为 {Sno, Cname}。也就是说，确定学生和课程之后，就能确定其它信息。</p>
<table>
<thead>
<tr>
<th>Sno</th>
<th>Sname</th>
<th>Sdept</th>
<th>Mname</th>
<th>Cname</th>
<th>Grade</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>学生-1</td>
<td>学院-1</td>
<td>院长-1</td>
<td>课程-1</td>
<td>90</td>
</tr>
<tr>
<td>2</td>
<td>学生-2</td>
<td>学院-2</td>
<td>院长-2</td>
<td>课程-2</td>
<td>80</td>
</tr>
<tr>
<td>2</td>
<td>学生-2</td>
<td>学院-2</td>
<td>院长-2</td>
<td>课程-1</td>
<td>100</td>
</tr>
<tr>
<td>3</td>
<td>学生-3</td>
<td>学院-2</td>
<td>院长-2</td>
<td>课程-2</td>
<td>95</td>
</tr>
</tbody></table>
<p>不符合范式的关系，会产生很多异常，主要有以下四种异常：</p>
<ul>
<li>冗余数据：例如 <code>学生-2</code> 出现了两次。</li>
<li>修改异常：修改了一个记录中的信息，但是另一个记录中相同的信息却没有被修改。</li>
<li>删除异常：删除一个信息，那么也会丢失其它信息。例如删除了 <code>课程-1</code> 需要删除第一行和第三行，那么 <code>学生-1</code> 的信息就会丢失。</li>
<li>插入异常：例如想要插入一个学生的信息，如果这个学生还没选课，那么就无法插入。</li>
</ul>
<h3 id="范式"><a href="#范式" class="headerlink" title="范式"></a>范式</h3><p>范式理论是为了解决以上提到四种异常。</p>
<p>高级别范式的依赖于低级别的范式，1NF 是最低级别的范式。</p>
<h4 id="1-第一范式-1NF"><a href="#1-第一范式-1NF" class="headerlink" title="1. 第一范式 (1NF)"></a>1. 第一范式 (1NF)</h4><p>1NF的定义为：符合1NF的关系中的每个属性都不可再分。1NF是所有关系型数据库的最基本要求。</p>
<p>但是仅仅符合1NF的设计，仍然会存在数据冗余过大，插入异常，删除异常，修改异常的问题。</p>
<h4 id="2-第二范式-2NF"><a href="#2-第二范式-2NF" class="headerlink" title="2. 第二范式 (2NF)"></a>2. 第二范式 (2NF)</h4><p>2NF在1NF的基础之上，消除了非主属性对于码的部分函数依赖。</p>
<p>每个非主属性完全函数依赖于键码，可以通过分解来满足。</p>
<p><strong>分解前</strong></p>
<table>
<thead>
<tr>
<th>Sno</th>
<th>Sname</th>
<th>Sdept</th>
<th>Mname</th>
<th>Cname</th>
<th>Grade</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>学生-1</td>
<td>学院-1</td>
<td>院长-1</td>
<td>课程-1</td>
<td>90</td>
</tr>
<tr>
<td>2</td>
<td>学生-2</td>
<td>学院-2</td>
<td>院长-2</td>
<td>课程-2</td>
<td>80</td>
</tr>
<tr>
<td>2</td>
<td>学生-2</td>
<td>学院-2</td>
<td>院长-2</td>
<td>课程-1</td>
<td>100</td>
</tr>
<tr>
<td>3</td>
<td>学生-3</td>
<td>学院-2</td>
<td>院长-2</td>
<td>课程-2</td>
<td>95</td>
</tr>
</tbody></table>
<p>以上学生课程关系中，{Sno, Cname} 为键码，有如下函数依赖：</p>
<ul>
<li>Sno -&gt; Sname, Sdept</li>
<li>Sdept -&gt; Mname</li>
<li>Sno, Cname-&gt; Grade</li>
</ul>
<p>Grade 完全函数依赖于键码，它没有任何冗余数据，每个学生的每门课都有特定的成绩。</p>
<p>Sname, Sdept 和 Mname 都部分依赖于键码，当一个学生选修了多门课时，这些数据就会出现多次，造成大量冗余数据。</p>
<p><strong>分解后</strong></p>
<p>关系-1</p>
<table>
<thead>
<tr>
<th>Sno</th>
<th>Sname</th>
<th>Sdept</th>
<th>Mname</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>学生-1</td>
<td>学院-1</td>
<td>院长-1</td>
</tr>
<tr>
<td>2</td>
<td>学生-2</td>
<td>学院-2</td>
<td>院长-2</td>
</tr>
<tr>
<td>3</td>
<td>学生-3</td>
<td>学院-2</td>
<td>院长-2</td>
</tr>
</tbody></table>
<p>有以下函数依赖：</p>
<ul>
<li>Sno -&gt; Sname, Sdept</li>
<li>Sdept -&gt; Mname</li>
</ul>
<p>关系-2</p>
<table>
<thead>
<tr>
<th>Sno</th>
<th>Cname</th>
<th>Grade</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>课程-1</td>
<td>90</td>
</tr>
<tr>
<td>2</td>
<td>课程-2</td>
<td>80</td>
</tr>
<tr>
<td>2</td>
<td>课程-1</td>
<td>100</td>
</tr>
<tr>
<td>3</td>
<td>课程-2</td>
<td>95</td>
</tr>
</tbody></table>
<p>有以下函数依赖：</p>
<ul>
<li>Sno, Cname -&gt; Grade</li>
</ul>
<h4 id="3-第三范式-3NF"><a href="#3-第三范式-3NF" class="headerlink" title="3. 第三范式 (3NF)"></a>3. 第三范式 (3NF)</h4><p>非主属性不传递函数依赖于键码。</p>
<p>3NF在2NF的基础之上，消除了非主属性对于码的传递函数依赖。也就是说， 如果存在非主属性对于码的传递函数依赖，则不符合3NF的要求。</p>
<p>符合3NF要求的数据库设计，基本上解决了数据冗余过大，插入异常，修改异常，删除异常的问题。当然，在实际中，往往为了性能上或者应对扩展的需要，经常 做到2NF或者1NF。</p>
<p>上面的 关系-1 中存在以下传递函数依赖：</p>
<ul>
<li>Sno -&gt; Sdept -&gt; Mname</li>
</ul>
<p>可以进行以下分解：</p>
<p>关系-11</p>
<table>
<thead>
<tr>
<th>Sno</th>
<th>Sname</th>
<th>Sdept</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>学生-1</td>
<td>学院-1</td>
</tr>
<tr>
<td>2</td>
<td>学生-2</td>
<td>学院-2</td>
</tr>
<tr>
<td>3</td>
<td>学生-3</td>
<td>学院-2</td>
</tr>
</tbody></table>
<p>关系-12</p>
<table>
<thead>
<tr>
<th>Sdept</th>
<th>Mname</th>
</tr>
</thead>
<tbody><tr>
<td>学院-1</td>
<td>院长-1</td>
</tr>
<tr>
<td>学院-2</td>
<td>院长-2</td>
</tr>
</tbody></table>
<h2 id="ER-图"><a href="#ER-图" class="headerlink" title="ER 图"></a>ER 图</h2><p>Entity-Relationship，有三个组成部分：实体、属性、联系。</p>
<p>用来进行关系型数据库系统的概念设计。</p>
<h3 id="实体的三种联系"><a href="#实体的三种联系" class="headerlink" title="实体的三种联系"></a>实体的三种联系</h3><p>包含一对一，一对多，多对多三种。</p>
<ul>
<li>如果 A 到 B 是一对多关系，那么画个带箭头的线段指向 B；</li>
<li>如果是一对一，画两个带箭头的线段；</li>
<li>如果是多对多，画两个不带箭头的线段。</li>
</ul>
<p>下图的 Course 和 Student 是一对多的关系。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgrwfhuj30f703y74a.jpg" alt="img" style="zoom:67%;" />



<h3 id="表示出现多次的关系"><a href="#表示出现多次的关系" class="headerlink" title="表示出现多次的关系"></a>表示出现多次的关系</h3><p>一个实体在联系出现几次，就要用几条线连接。</p>
<p>下图表示一个课程的先修关系，先修关系出现两个 Course 实体，第一个是先修课程，后一个是后修课程，因此需要用两条线来表示这种关系。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgsso8aj309j079wel.jpg" alt="img" style="zoom:67%;" />



<h3 id="联系的多向性"><a href="#联系的多向性" class="headerlink" title="联系的多向性"></a>联系的多向性</h3><p>虽然老师可以开设多门课，并且可以教授多名学生，但是对于特定的学生和课程，只有一个老师教授，这就构成了一个三元联系。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgt8i9mj30fd06omx9.jpg" alt="img" style="zoom:67%;" />



<h3 id="表示子类"><a href="#表示子类" class="headerlink" title="表示子类"></a>表示子类</h3><p>用一个三角形和两条线来连接类和子类，与子类有关的属性和联系都连到子类上，而与父类和子类都有关的连到父类上。</p>
<img src="https://tva1.sinaimg.cn/large/007S8ZIlgy1gf6pgu7d52j30l8090t92.jpg" alt="img" style="zoom:67%;" />

<h2 id="三级模式与两级映射"><a href="#三级模式与两级映射" class="headerlink" title="三级模式与两级映射"></a>三级模式与两级映射</h2><h3 id="三级模式"><a href="#三级模式" class="headerlink" title="三级模式"></a>三级模式</h3><p>人们为数据库设计了一个严谨的体系结构，数据库领域公认的标准结构是三级模式结构，它包括外模式、概念模式、内模式，有效地组织、管理数据，提高了数据库的逻辑独立性和物理独立性。</p>
<h4 id="用户级–-gt-外模式-反映了数据库系统的用户观"><a href="#用户级–-gt-外模式-反映了数据库系统的用户观" class="headerlink" title="用户级–&gt; 外模式(反映了数据库系统的用户观)"></a>用户级–&gt; 外模式(反映了数据库系统的用户观)</h4><p>外模式又称子模式或用户模式，对应于用户级。<strong>它是某个或某几个用户所看到的数据库的数据视图，是与某一应用有关的数据的逻辑表示。</strong>外模式是从模式导出的一个子集，包含模式中允许特定用户使用的那部分数据。用户可以通过外模式描述语言来描述、定义对应于用户的数据记录(外模式)，也可以利用数据操纵语言(Data Manipulation Language，DML)对这些数据记录进行操作。</p>
<h4 id="概念级–-gt-概念模式（反映了数据库系统的整体观）"><a href="#概念级–-gt-概念模式（反映了数据库系统的整体观）" class="headerlink" title="概念级–&gt; 概念模式（反映了数据库系统的整体观）"></a>概念级–&gt; 概念模式（反映了数据库系统的整体观）</h4><p>概念模式又称模式或逻辑模式，对应于概念级。<strong>它是由数据库设计者综合所有用户的数据，按照统一的观点构造的全局逻辑结构，是对数据库中全部数据的逻辑结构和特征的总体描述，是所有用户的公共数据视图(全局视图)。</strong>它是由数据库管理系统提供的数据模式描述语言(Data Description Language，DDL)来描述、定义的。</p>
<h4 id="物理级-–-gt-内模式（反映了数据库系统的存储观）"><a href="#物理级-–-gt-内模式（反映了数据库系统的存储观）" class="headerlink" title="物理级 –&gt; 内模式（反映了数据库系统的存储观）"></a>物理级 –&gt; 内模式（反映了数据库系统的存储观）</h4><p>内模式又称存储模式，对应于物理级。<strong>它是数据库中全体数据的内部表示或底层描述，是数据库最低一级的逻辑描述，它描述了数据在存储介质上的存储方式和物理结构，对应着实际存储在外存储介质上的数据库。</strong>内模式由内模式描述语言来描述、定义的。</p>
<h3 id="两级映射"><a href="#两级映射" class="headerlink" title="两级映射"></a>两级映射</h3><p>数据库系统在三级模式之间提供了两级映像：模式/内模式的映像、外模式/模式的映像。</p>
<ul>
<li>模式/内模式的映像：实现概念模式到内模式之间的相互转换。</li>
<li>外模式/模式的映像：实现外模式到概念模式之间的相互转换。</li>
</ul>

    </div>

    
    
    
        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>yanbing
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="http://yoursite.com/2020/05/30/%E6%95%B0%E6%8D%AE%E5%BA%93/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86/" title="数据库系统原理">http://yoursite.com/2020/05/30/数据库/数据库系统原理/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2020/05/14/%E4%BA%BA%E5%8A%9B%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E9%A1%B9%E7%9B%AE%E7%AC%94%E8%AE%B0/19.SpringBoot%E4%B8%AD%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8F%82%E6%95%B0%E7%BB%91%E5%AE%9A/" rel="prev" title="19.SpringBoot中自定义参数绑定">
      <i class="fa fa-chevron-left"></i> 19.SpringBoot中自定义参数绑定
    </a></div>
      <div class="post-nav-item">
    <a href="/2020/05/30/Java%E5%9F%BA%E7%A1%80/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/" rel="next" title="Java多线程">
      Java多线程 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#事务"><span class="nav-number">1.</span> <span class="nav-text">事务</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#概念"><span class="nav-number">1.1.</span> <span class="nav-text">概念</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ACID特性"><span class="nav-number">1.2.</span> <span class="nav-text">ACID特性</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-原子性（Atomicity）"><span class="nav-number">1.2.1.</span> <span class="nav-text">1. 原子性（Atomicity）</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-一致性（Consistency）"><span class="nav-number">1.2.2.</span> <span class="nav-text">2. 一致性（Consistency）</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-隔离性（Isolation）"><span class="nav-number">1.2.3.</span> <span class="nav-text">3. 隔离性（Isolation）</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-持久性（Durability）"><span class="nav-number">1.2.4.</span> <span class="nav-text">4. 持久性（Durability）</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#AUTOCOMMIT"><span class="nav-number">1.3.</span> <span class="nav-text">AUTOCOMMIT</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#并发一致性问题"><span class="nav-number">2.</span> <span class="nav-text">并发一致性问题</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#丢失修改"><span class="nav-number">2.1.</span> <span class="nav-text">丢失修改</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#读脏数据"><span class="nav-number">2.2.</span> <span class="nav-text">读脏数据</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#不可重复读"><span class="nav-number">2.3.</span> <span class="nav-text">不可重复读</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#幻影读"><span class="nav-number">2.4.</span> <span class="nav-text">幻影读</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#封锁"><span class="nav-number">3.</span> <span class="nav-text">封锁</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#封锁粒度"><span class="nav-number">3.1.</span> <span class="nav-text">封锁粒度</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#封锁类型"><span class="nav-number">3.2.</span> <span class="nav-text">封锁类型</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-读写锁"><span class="nav-number">3.2.1.</span> <span class="nav-text">1. 读写锁</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-意向锁"><span class="nav-number">3.2.2.</span> <span class="nav-text">2. 意向锁</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#封锁协议"><span class="nav-number">3.3.</span> <span class="nav-text">封锁协议</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#三级封锁协议"><span class="nav-number">3.3.1.</span> <span class="nav-text">三级封锁协议</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#两段封锁协议"><span class="nav-number">3.3.2.</span> <span class="nav-text">两段封锁协议</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#MySQL-隐式与显示锁定"><span class="nav-number">3.4.</span> <span class="nav-text">MySQL 隐式与显示锁定</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#隔离级别"><span class="nav-number">4.</span> <span class="nav-text">隔离级别</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#未提交读（READ-UNCOMMITTED）"><span class="nav-number">4.1.</span> <span class="nav-text">未提交读（READ UNCOMMITTED）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#提交读（READ-COMMITTED）"><span class="nav-number">4.2.</span> <span class="nav-text">提交读（READ COMMITTED）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#可重复读（REPEATABLE-READ）"><span class="nav-number">4.3.</span> <span class="nav-text">可重复读（REPEATABLE READ）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#可串行化（SERIALIZABLE）"><span class="nav-number">4.4.</span> <span class="nav-text">可串行化（SERIALIZABLE）</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#多版本并发控制"><span class="nav-number">5.</span> <span class="nav-text">多版本并发控制</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#基本思想"><span class="nav-number">5.1.</span> <span class="nav-text">基本思想</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#版本号"><span class="nav-number">5.2.</span> <span class="nav-text">版本号</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Undo-日志"><span class="nav-number">5.3.</span> <span class="nav-text">Undo 日志</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ReadView"><span class="nav-number">5.4.</span> <span class="nav-text">ReadView</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#快照读与当前读"><span class="nav-number">5.5.</span> <span class="nav-text">快照读与当前读</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-快照读"><span class="nav-number">5.5.1.</span> <span class="nav-text">1. 快照读</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-当前读"><span class="nav-number">5.5.2.</span> <span class="nav-text">2. 当前读</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Next-Key-Locks"><span class="nav-number">6.</span> <span class="nav-text">Next-Key Locks</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Record-Locks"><span class="nav-number">6.1.</span> <span class="nav-text">Record Locks</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Gap-Locks"><span class="nav-number">6.2.</span> <span class="nav-text">Gap Locks</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Next-Key-Locks-1"><span class="nav-number">6.3.</span> <span class="nav-text">Next-Key Locks</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#关系数据库设计理论"><span class="nav-number">7.</span> <span class="nav-text">关系数据库设计理论</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#函数依赖"><span class="nav-number">7.1.</span> <span class="nav-text">函数依赖</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#异常"><span class="nav-number">7.2.</span> <span class="nav-text">异常</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#范式"><span class="nav-number">7.3.</span> <span class="nav-text">范式</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-第一范式-1NF"><span class="nav-number">7.3.1.</span> <span class="nav-text">1. 第一范式 (1NF)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-第二范式-2NF"><span class="nav-number">7.3.2.</span> <span class="nav-text">2. 第二范式 (2NF)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-第三范式-3NF"><span class="nav-number">7.3.3.</span> <span class="nav-text">3. 第三范式 (3NF)</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ER-图"><span class="nav-number">8.</span> <span class="nav-text">ER 图</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#实体的三种联系"><span class="nav-number">8.1.</span> <span class="nav-text">实体的三种联系</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#表示出现多次的关系"><span class="nav-number">8.2.</span> <span class="nav-text">表示出现多次的关系</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#联系的多向性"><span class="nav-number">8.3.</span> <span class="nav-text">联系的多向性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#表示子类"><span class="nav-number">8.4.</span> <span class="nav-text">表示子类</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#三级模式与两级映射"><span class="nav-number">9.</span> <span class="nav-text">三级模式与两级映射</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#三级模式"><span class="nav-number">9.1.</span> <span class="nav-text">三级模式</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#用户级–-gt-外模式-反映了数据库系统的用户观"><span class="nav-number">9.1.1.</span> <span class="nav-text">用户级–&gt; 外模式(反映了数据库系统的用户观)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#概念级–-gt-概念模式（反映了数据库系统的整体观）"><span class="nav-number">9.1.2.</span> <span class="nav-text">概念级–&gt; 概念模式（反映了数据库系统的整体观）</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#物理级-–-gt-内模式（反映了数据库系统的存储观）"><span class="nav-number">9.1.3.</span> <span class="nav-text">物理级 –&gt; 内模式（反映了数据库系统的存储观）</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#两级映射"><span class="nav-number">9.2.</span> <span class="nav-text">两级映射</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="yanbing"
      src="/images/dog.jpg">
  <p class="site-author-name" itemprop="name">yanbing</p>
  <div class="site-description" itemprop="description">闲看庭前花开落，漫随天外云卷舒。</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">59</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">14</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">9</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/yanbingzn" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;yanbingzn" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://blog.csdn.net/i_silence" title="CSDN → https:&#x2F;&#x2F;blog.csdn.net&#x2F;i_silence" rel="noopener" target="_blank"><i class="fab fa-codiepie fa-fw"></i>CSDN</a>
      </span>
  </div>
  <div class="cc-license motion-element" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" rel="noopener" target="_blank"><img src="/images/cc-by-nc-sa.svg" alt="Creative Commons"></a>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        
  <div class="beian"><a href="http://www.beian.miit.gov.cn/" rel="noopener" target="_blank">豫ICP备20019377号 </a>
  </div>

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">yanbing</span>
</div>

<span id="busuanzi_container_site_uv">
  本站访问次数：<span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
</span>

        
<div class="busuanzi-count">
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <span class="post-meta-item" id="busuanzi_container_site_uv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-user"></i>
      </span>
      <span class="site-uv" title="总访客量">
        <span id="busuanzi_value_site_uv"></span>
      </span>
    </span>
</div>








      </div>
    </footer>
  </div>

  
  <script size="300" alpha="0.6" zIndex="-1" src="/lib/canvas-ribbon/canvas-ribbon.js"></script>
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  
  <script>
    (function(){
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x=document.getElementsByTagName("link");
		//Find the last canonical URL
		if(x.length > 0){
			for (i=0;i<x.length;i++){
				if(x[i].rel.toLowerCase() == 'canonical' && x[i].href){
					canonicalURL=x[i].href;
				}
			}
		}
    //Get protocol
	    if (!canonicalURL){
	    	curProtocol = window.location.protocol.split(':')[0];
	    }
	    else{
	    	curProtocol = canonicalURL.split(':')[0];
	    }
      //Get current URL if the canonical URL does not exist
	    if (!canonicalURL) canonicalURL = window.location.href;
	    //Assign script content. Replace current URL with the canonical URL
      !function(){var e=/([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,r=canonicalURL,t=document.referrer;if(!e.test(r)){var n=(String(curProtocol).toLowerCase() === 'https')?"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif":"//api.share.baidu.com/s.gif";t?(n+="?r="+encodeURIComponent(document.referrer),r&&(n+="&l="+r)):r&&(n+="?l="+r);var i=new Image;i.src=n}}(window);})();
  </script>




  
<script src="/js/local-search.js"></script>













  

  


  
  <script type="text/javascript"
color="0,0,188" opacity='0.5' zIndex="-1" count="120" src="//cdn.bootcss.com/canvas-nest.js/1.0.0/canvas-nest.min.js"></script>
  

</body>
</html>
